/*=+--+=#=+--     Unmanned Aerial System Management Software      --+=#=+--+=#*\
|          Copyright (C) 2011 Regents of the University of Colorado.           |
|                             All Rights Reserved.                             |

     This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU General Public License version 2 as
     published by the Free Software Foundation.

     This program is distributed in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     GNU General Public License for more details.

     You should have received a copy of the GNU General Public License
     along with this program.  If not, see <http://www.gnu.org/licenses/>.

            Jack Elston                       Cory Dixon                        
|           elstonj@colorado.edu              dixonc@colorado.edu              |
|                                                                              |
\*=+--+=#=+--                 --+=#=+--+=#=+--                    --+=#=+--+=#*/

/*!
	\file radarManager.cxx

	\author Cory Dixon
*/
#include <iostream>
#include <fstream>
#include <sstream>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>
#include <vector>
#include <string>

#include "radarSimLayer.h"
#include "clientList.h"
#include "vcgui3.h"
#include "utilities.h"
#include "vcConfig.h"
#include "geoRefMap.h"
#include "color.h"

using namespace std;

/*<---Global Variables---->*/
extern GuiStruct guiStr;

// just an upper bound saftey limit
#define MAX_NUMBER_RADAR_IMGS 50  

//---------------------------
// WE use NOAAs radar.weather.gov interface
//---------------------------
#define RADAR_BASE_DIR   VCGUI_DIR"/radar/RadarImg/"
#define RADAR_CACHE_DIR  RADAR_BASE_DIR"#D/#S/"
#define RADAR_TYPE_DIR   RADAR_BASE_DIR"#D/#S"
#define RADAR_CACHE_BASE RADAR_BASE_DIR"#D/#S/#S_#D_0.gif"
#define RADAR_CACHE_GFW  RADAR_BASE_DIR"#D/#S.gfw"

#define RADAR_SITE       "http://128.138.75.190/"
#define RADAR_BASE       RADAR_SITE"RadarImg/#D/"
#define RADAR_BASE_GFW   RADAR_SITE"RadarImg/#D/#S_#D_0.gfw"
#define RADAR_IMGS       RADAR_SITE"RadarImg/#D/#S/"
#define RADAR_OVERLAYS   RADAR_SITE"Overlays/"
#define RADAR_WARNINGS   RADAR_SITE"Warnings/Short/"
#define RADAR_WARNINGS_GFW  RADAR_SITE"Warnings/Short/#S_Warnings_0.gfw"
#define RADAR_LEGEND     RADAR_SITE"Legend/#D/#S/"

#define LFTP_RADAR           "lftp -c \"cache off; mirror --delete "RADAR_IMGS" "RADAR_CACHE_DIR"\" 2> /dev/null &"
#define LFTP_RADAR_BASE      "lftp -c \"cache off; mirror -i #S_#D_0.gif -r "RADAR_BASE" "RADAR_TYPE_DIR"\" 2> /dev/null &"
#define LFTP_RADAR_BASE_GFW  "lftp -c \"cache expire 300; get "RADAR_BASE_GFW" -o "RADAR_CACHE_GFW"\" 2> /dev/null &"
#define LFTP_RADAR_WARNINGS  "lftp -c \"cache off; mirror -i #S_Warnings_0.gif -r "RADAR_WARNINGS" "RADAR_TYPE_DIR"\" 2> /dev/null &"
#define LFTP_RADAR_WARNINGS_GFW  "lftp -c \"cache expire 300; get "RADAR_WARNINGS_GFW" -o "RADAR_CACHE_GFW"\" " 
//2> /dev/null &"

//---------------------------//
#define URI_MARKER_S    "#S"
#define URI_MARKER_D    "#D"

#define URI_HAS_S    (1 << 0)
#define URI_HAS_D    (1 << 1)
//...
#define URI_FLAG_END (1 << 2)

RadarImageType_t const RadarSimManager::radarTypes[NUM_RADAR_TYPES] = {
	{"N0R", "Base Reflectivity"},
	{"N0Z", "Long Range Reflectivity"},
	{"NCR", "Composite Reflectivity"},
	{"N0V", "Base Velocity"},
	{"N0S", "Storm Relative Motion"},
	{"N1P", "1 Hour Precipitation"},
	{"NTP", "Storm Total Precipitation"},
	{"Warnings", "Storm & Severe Weather Warnings"},
};

RadarSimManager::RadarSimManager() : LayerManager()
{
	radar_list     = NULL;
	site_group     = NULL;
	site_group_h   = NULL;
	img_group      = NULL;
	layerG         = NULL;
	radar_window   = NULL;
	radar_subs     = NULL;
	radar_layer    = NULL;
	tile_queue     = NULL;
	scale_item     = NULL;
	scale_item_h   = NULL;
	active_station = NULL;

	timeScale      = 120.0; // update in radar time in seconds per display update
	time_offset    = 0;   // time off set in minutes for radar

	playing        = false;
	user_min_time  = 0;
}

RadarSimManager::~RadarSimManager()
{	
	// loop through radar sites
	for(unsigned int ind=0; ind < g_list_length(radar_list); ind++) {
		NexradStations_t *station = (NexradStations_t *)g_list_nth_data(radar_list,ind);
		if( station ) delete station;
	}

	if( radar_list ) g_list_free( radar_list );
	radar_list = 0;

	for(unsigned int ind=0; ind < g_list_length(radar_subs); ind++) {
		char *uri = (char *)g_list_nth_data(radar_subs,ind);
		if( uri ) g_free(uri);
	}
	if( radar_subs ) g_list_free( radar_subs );
	radar_subs = 0;
}

void RadarSimManager::init(GPSMap *map)
{
	// init base class
	LayerManager::init(map);
}

void RadarSimManager::one_time_init()
{
	cout << "Initializing Simulated Radar : ..............";

	// get my layer group
	layerG = GOO_CANVAS_ITEM(gps_map->getLayerG(GPSMap::RADAR_SIM_LAYER));
	LayerManager::setShown(true);

	img_group = goo_canvas_group_new (layerG, NULL);
	site_group_h = goo_canvas_group_new (layerG, NULL);

	// add the image to the layer list
	string name = "NEXRAD Sites";
	gps_map->addLayer( name.c_str(), site_group_h, (GooCanvasItem *)GPSMap::RADAR_SIM_LAYER );
	gps_map->setLayerListShown(site_group_h, true );

	scale_item_h = GOO_CANVAS_ITEM(goo_canvas_group_new (gps_map->getRootItem(), NULL));
	name = "Time Scale";
	gps_map->addLayer( name.c_str(), scale_item_h, (GooCanvasItem *)GPSMap::RADAR_SIM_LAYER );
	gps_map->setLayerListShown(scale_item_h, true );

	// attempt to get the file attributes, i.e. test file existence
	//int ret;
	struct stat stFileInfo;
	if( stat(RADAR_BASE_DIR, &stFileInfo) != 0 ) {
		cout << "Creating radar directory structure : ";

		if( mkdir(VCGUI_DIR"/radar", 0775) < 0 )
			if( errno != EEXIST ) perror("Could not create base radar directory");

		if( mkdir(RADAR_BASE_DIR, 0775) < 0 )
			if( errno != EEXIST ) perror("Could not create directory for radar images");

		if( errno == 0 )
			cout << " ....... [" GREEN << "done" << ATTROFF "]" << endl;
		else
			cout << " ....... [" RED << "error" << ATTROFF "]" << endl;
	}

	// update maps every 5 seconds
	g_timeout_add (5*1000, RadarSimManager::update_web_timeout, this);
	// update time-loop of loading radar files every 5 seconds
	g_timeout_add (5*1000, RadarSimManager::update_files_timeout, this);
	// update time-loop of displaying images at 5 Hz
	g_timeout_add (200, RadarSimManager::update_image_timeout, this);

	NexradStations_t *station = new NexradStations_t;
	radar_list = g_list_prepend( radar_list, station);

	station->icao   = "KSIM";
	station->name   = "JACK ELSTON";
	station->st     = "CO";
	station->county = "UNITED STATES";
	station->lat    = 40.007792;
	station->lon    = -105.261772;
	station->elev   = 5430;
	station->time   = +7;

	// reorder list back to that in file
	radar_list = g_list_reverse( radar_list );

	cout << " [done]" << endl;
}

void RadarSimManager::create_canvas()
{
	double coord_x, coord_y;

	//cout << " RadarSimManager::create_canvas - site_group= " << site_group << endl;
	
	// see if we are initialized
	if( !layerG ) one_time_init();
	if( site_group ) return;
	site_group = goo_canvas_group_new (site_group_h, NULL);

	// loop through radar sites
	for(unsigned int ind=0; ind < g_list_length(radar_list); ind++) {
		NexradStations_t *station = (NexradStations_t *)g_list_nth_data(radar_list,ind);

		coord_y = station->lat;
		coord_x = station->lon;

		// transform into window
		gps_map->geoToLayer(GPSMap::RADAR_SIM_LAYER, coord_x, coord_y);

		double delta_pix = 229648.0 / gps_map->mPerP( station->lat ); // 124 nautical miles

		station->station_item = goo_canvas_group_new (site_group, 
				"x", coord_x, "y", coord_y, 
				"can-focus", TRUE, 
				NULL);
		setup_item(station->station_item);

		// text - index number
		station->text_item= goo_canvas_text_new (
				station->station_item,
				station->icao.c_str(), 0.0, 0.0, -1,
				GTK_ANCHOR_CENTER,
				"fill_color", "midnight blue",
				"font", "Sans 10px",
				NULL);

		station->range_item = goo_canvas_ellipse_new( 
				station->station_item,
				0.0, 0.0,
				delta_pix, delta_pix,
				"stroke-color-rgba", 0x11111144,
				"fill-color-rgba", 0xFFFF0044,
				"line-width", 2.0,
				"can-focus", TRUE,
				NULL );

		g_signal_connect (station->range_item, "enter-notify-event", G_CALLBACK (on_focus_in), this);
		g_signal_connect (station->range_item, "leave-notify-event", G_CALLBACK (on_focus_out), this);

	}

	// set the layer as active and shown
	gps_map->setLayerListShown( (GooCanvasItem *)GPSMap::RADAR_SIM_LAYER, true );

	// make sure parent manager layer is shown
	gps_map->toggleLayerShown(GPSMap::RADAR_SIM_LAYER, true);
}

gboolean RadarSimManager::update_image_timeout(gpointer data)
{
	if( !data ) return false;
	RadarSimManager * radar = (RadarSimManager *)data;
	//cout << "RadarSimManager::update_image_timeout - radar=" << radar << endl;
	if( goo_canvas_item_is_visible( radar->layerG ) ) 
		radar->update_images();
	return true;
}

void RadarSimManager::update_images()
{
	//cout << "RadarSimManager::update_images -- time=" << time_offset << endl;

	min_time  = 0;
	last_time = 0; 

	// remove the sites since they slow down panning if not visible
	if( site_group_h && !goo_canvas_item_is_visible( site_group_h ) ) {
		if( site_group ) {
			goo_canvas_item_remove(site_group);
			site_group = NULL;
			g_object_set(site_group_h,"visibility", GOO_CANVAS_ITEM_INVISIBLE,NULL);
		}
	} else if( !site_group && site_group_h && goo_canvas_item_is_visible( site_group_h ) && goo_canvas_item_is_visible(layerG)) {
		create_canvas();
	}

	// loop through stations, and see if we are subscribed
	for(unsigned int ind=0; ind < g_list_length(radar_list); ind++) 
	{
		// get the site
		NexradStations_t *station = (NexradStations_t *)g_list_nth_data(radar_list,ind);

		// remove old image
		if( !station->img_layer ) {
			for( int t=0; t < NUM_RADAR_TYPES; t++ ){
				if( station->img_item[t] ) goo_canvas_item_remove( station->img_item[t] );
				station->img_item[t] = NULL;
			}
			continue;
		}

		// now loop through radar types
		for( int tInd=0; tInd < NUM_RADAR_TYPES; tInd++ )
		{
			if( station->img_item_h[tInd] && !goo_canvas_item_is_visible(station->img_item_h[tInd]) ) continue;

			RadarImgAlloc_t *alloc = NULL;
			RadarImgAlloc_t *currImgalloc = NULL;
			if( station->getLooped(tInd) ) station->currImgTod[tInd] = 0;
			for(unsigned int f_ind=0; f_ind < g_list_length(station->img_alloc_list[tInd]); f_ind++) {
				// get the file
				alloc = (RadarImgAlloc_t *)g_list_nth_data(station->img_alloc_list[tInd],f_ind);
				if( alloc ) {
					string f = getFileName(alloc->file.c_str());

					time_t file_tt;
					struct tm file_tm;

					//-- if looped, then file names contain actual time of image in UTC
					if( station->getLooped(tInd) ) {
						int year  = atoi(f.substr(4,4).c_str());
						int month = atoi(f.substr(8,2).c_str());
						int day   = atoi(f.substr(10,2).c_str());
						int h     = atoi(f.substr(13,2).c_str());
						int m     = atoi(f.substr(15,2).c_str());

						file_tm.tm_sec   = 0;           /* seconds after the minute - [0,59] */
						file_tm.tm_min   = m;           /* minutes after the hour - [0,59] */
						file_tm.tm_hour  = h;           /* hours since midnight - [0,23] */
						file_tm.tm_mday  = day;         /* day of the month - [1,31] */
						file_tm.tm_mon   = month - 1;   /* months since January - [0,11] */
						file_tm.tm_year  = year - 1900; /* years since 1900 */
						file_tm.tm_isdst = -1;          /* daylight savings time */

						file_tt = mktime( &file_tm );

						// mktime assumes localtime, so account for timezone
						// since file time is in UTC
						file_tt += file_tm.tm_gmtoff;

					//-- if snap shot, have to take file time 
					} else {
						file_tt = station->currImgTod[tInd];
					}

					// get local time
					//time_t tt = ::time(NULL);
					//cout << "File Time: " << asctime(localtime(&file_tt)) << endl;
					//if( tt > file_tt + 8*3600 )
						//cout << " > 8 hour old "<< endl;

					if( file_tt > last_time ) { 
						last_time = file_tt;
					}
					if( file_tt < min_time || min_time == 0) { 
						min_time = file_tt;
					}

					if( station->getLooped(tInd) && (file_tt <= time_offset && file_tt > station->currImgTod[tInd]) ) {
						currImgalloc = alloc;
						station->currImgTod[tInd] = file_tt;
					} else if( !station->getLooped(tInd) ) { 
						currImgalloc = alloc;
					}
				}
			}

			if( currImgalloc ) { 
				// we are only getting the real pointer, not a copy so don't unref
				GdkPixbuf *pixbuf = currImgalloc->geoImg->getPixbuf();

				// see if the pixbuf is loaded
				if( pixbuf == station->loaded_pix[tInd] )
					continue;

				station->loaded_pix[tInd] = pixbuf;

				if( station->img_item[tInd] ) goo_canvas_item_remove( station->img_item[tInd] );
				station->img_item[tInd] = NULL;

				//-- make the image a transparent overlay
				GdkPixbuf *a1 = gdk_pixbuf_add_alpha(pixbuf, false, 0,0,0);
				if(tInd != RADAR_WARN) {
					int rowstride, n_channels;
					uint8_t *pixels, *p;

					rowstride     = gdk_pixbuf_get_rowstride (a1);
					pixels        = gdk_pixbuf_get_pixels (a1);
					n_channels    = gdk_pixbuf_get_n_channels (a1);

					uint8_t alpha = 140;

					for( int y=0; y < gdk_pixbuf_get_height(a1); y++ ) {
						for( int x=0; x < gdk_pixbuf_get_width(a1); x++ ) {
							p = pixels + y * rowstride + x * n_channels;
							if( p[3] > alpha ) p[3] = alpha;
						}
					}
				}

				// create new image group on the radar image layer
				if( !station->img_item_h[tInd] ) {
					station->img_item_h[tInd] = goo_canvas_group_new( GOO_CANVAS_ITEM(station->img_layer), NULL);
					//-- add the image to the layer list
					gps_map->addLayer( radarTypes[tInd].type, station->img_item_h[tInd], 
					                   station->img_layer, radarTypes[tInd].name);
					gps_map->setLayerListShown(station->img_item_h[tInd], true );
				}
				station->img_item[tInd] = goo_canvas_image_new( GOO_CANVAS_ITEM(station->img_item_h[tInd]), 
				                                                a1, 0.0, 0.0, NULL);
				g_object_unref (a1);

				// now determine scale width/height
				currImgalloc->scale_x = currImgalloc->geoImg->getDegPerXp() / guiStr.config.gpsmap->degPerXp();
				currImgalloc->scale_y = currImgalloc->geoImg->getDegPerYp() / guiStr.config.gpsmap->degPerYp(station->lat); 
				//cout << "scale_x=" << currImgalloc->scale_x << " scale_y=" << currImgalloc->scale_y << endl;

				// convert geographic to base map image coordinates,
				double new_x;
				double new_y;
				currImgalloc->geoImg->getTiePoints(new_x, new_y);
				guiStr.config.gpsmap->geoToLayer(GPSMap::RADAR_SIM_LAYER, new_x, new_y);
				//cout << "img coord: x=" << new_x << " y=" << new_y << endl;

				// this is the offset for this map
				currImgalloc->x_off = new_x;
				currImgalloc->y_off = new_y;

				if( station->img_item[tInd] ) {
					// scale and translate image
					goo_canvas_item_translate(station->img_item[tInd], currImgalloc->x_off, currImgalloc->y_off);
					goo_canvas_item_scale(station->img_item[tInd], currImgalloc->scale_x, currImgalloc->scale_y);
					goo_canvas_item_request_update(station->img_item[tInd]);
				} else
					cout << "error getting canvas item from pixbuf" << endl;
			//} else {
				//if( time_offset > 0 )
					//cout << "\t!! did not find a radar image older than: " << asctime(localtime(&time_offset)) << endl;
			}
		} // radar types
	}

	// update time scale
	update_display_time();
}

void RadarSimManager::update_display_time(int dir, bool userStep)
{
	if( dir >= 0 ) dir = 1;
	else if( dir < 0 ) dir = -1;

	if( user_min_time < min_time )
		user_min_time = min_time;

	if( playing || userStep) {
		time_offset += (int)(dir * timeScale);
		if( time_offset > last_time )
			time_offset = user_min_time;		 
		else if( time_offset < user_min_time )
			time_offset = last_time;
	}

	// if there is a valid time scale, show it
	if( last_time > 0 && min_time <= last_time)
		setup_scale(min_time, user_min_time, time_offset, last_time);
	else if( scale_item ) {
		goo_canvas_item_remove(scale_item);
		scale_item = NULL;
	}

	//if( last_time >= 0 )
	//cout << " min time=" << min_time << " radar time=" << time_offset << " max time=" << last_time << endl;
}

void RadarSimManager::updateMap()
{
	//cout << "RadarSimManager::updateMap()" << endl;
}

gboolean RadarSimManager::update_files_timeout( gpointer data )
{
	if( !data ) return false;
	RadarSimManager *radar = (RadarSimManager*) data;

	//cout << "RadarSimManager::update_files_timeout - radar=" << radar << endl;

	if( goo_canvas_item_is_visible( radar->layerG ) ) 
		radar->update_files();
	return true;
}
	
void RadarSimManager::update_files( )
{
	//cout << "RadarSimManager::update_files" << endl;
	
	// loop through stations, and see if we are subscribed
	for(unsigned int ind=0; ind < g_list_length(radar_list); ind++) {

		// get the site
		NexradStations_t *station = (NexradStations_t *)g_list_nth_data(radar_list,ind);

		for( unsigned int type = 0; type < NUM_RADAR_TYPES; type++) {
			if( station->getSubscribed(type) ) {
				char *uri_dir    = replace_uri(RADAR_CACHE_DIR, station->icao.substr(1,3).c_str(), radarTypes[type].type);
				char *gfw        = replace_uri(RADAR_CACHE_GFW, station->icao.substr(1,3).c_str(), radarTypes[type].type);
				char *uri_base_f = replace_uri(RADAR_CACHE_BASE, station->icao.substr(1,3).c_str(), radarTypes[type].type);

				//cout << "Looking for maps in: "<< uri << endl;
				//cout << "Looking for maps : "<< station->icao << endl;

				vector<string> files = vector<string>();
				getdir(uri_dir, files);

				// loop through files in directory
				for (unsigned int i = 0; i < files.size();i++) {

					// skip '.' and '..' directories
					if( files[i] == "." || files[i] == ".." )
						continue;

					//cout << "Dir: " << files[i] << endl;
					unsigned int f_ind;
					bool fileFound = false;
					RadarImgAlloc_t *alloc = NULL;

					//-- look to see if the file is loaded
					for(f_ind=0; f_ind < g_list_length(station->img_alloc_list[type]); f_ind++) {
						// get the file
						alloc = (RadarImgAlloc_t *)g_list_nth_data(station->img_alloc_list[type],f_ind);
						if( getFileName(alloc->file.c_str()) == files[i] )
							break;
					}
					if( f_ind < g_list_length(station->img_alloc_list[type]) ) 
						fileFound = true;

					time_t file_tt;
					//-- if this is the base file, it is the same name. So check that the 
					//-- modification time is newer than our current image
					//cout << "checking file times" << endl;
					string f = string(uri_dir) + files[i];
					struct stat attrib;			// create a file attribute structure
					struct tm file_tm;

					// get the attributes of the file
					if( stat(f.c_str(), &attrib) == -1 ) {
						//perror("RadarSimManager::update_files - stat");
						// remove the bad file
						if( remove( f.c_str() ) < 0 ) {
							cout << "Radar File: " << f << " -- could not remove file" << endl;
							//perror("could not remove file");
						}
						continue;
					} 
					file_tt = attrib.st_mtime;	// Get the last modified time and put it into the time structure

					(void)gmtime_r( &file_tt, &file_tm); // convert localtime to UTC
					file_tt = mktime( &file_tm ); // convert broken down time back to time_t

					// mktime assumes localtime, so account for timezone
					// since file time is in UTC
					//file_tt += file_tm.tm_gmtoff;
					file_tt -= timezone;

					// get local time
					time_t tt = ::time(NULL);

					//cout << "File Time: " << asctime(localtime(&file_tt)) << endl;
					if( tt > file_tt + 4*3600 ) {
						//cout << " > 4 hour old "<< endl;
						if( remove( f.c_str() ) < 0 ) {
							cout << "Radar File: " << f << " -- could not remove file" << endl;
							//perror("could not remove file");
						}
						if( fileFound ) {	
							// unload old file that is now dated
							station->img_alloc_list[type] = g_list_remove(station->img_alloc_list[type], alloc);
							delete alloc;
							//alloc = NULL;
						}

						continue;
					}

					if( fileFound && !station->getLooped(type) ) {
						//cout << "file: " << file_tt << " currImg: " << station->currImgTod[type] << endl;
						if( file_tt > station->currImgTod[type] ) {
							//cout << "Refreshing file: " << files[i] << endl;
							alloc->geoImg->Open();
							station->currImgTod[type] = file_tt;
						}
					} 

					//-- did not find file, so load it
					if( !fileFound ) {
						string f = string(uri_dir) + files[i];

						//cout << "\tf=" << f << endl << "\turi_base_f=" << uri_base_f << endl;
						if( (f != uri_base_f && station->getLooped(type)) || (f == uri_base_f && !station->getLooped(type)) ) {
							//cout << "Loading file: " << files[i] << endl;
							RadarImgAlloc_t * imgAlloc = RadarSimManager::addImage( f, gfw );
							if( imgAlloc ) {
								station->img_alloc_list[type] = g_list_append(station->img_alloc_list[type], imgAlloc);
								if( station->getLooped(type) ) 
									station->currImgTod[type] = 0;
								else
									station->currImgTod[type] = file_tt;
							} else {
								// remove the bad file
								if( remove( f.c_str() ) < 0 ) {
									cout << "Radar File: " << f << " -- could not remove file" << endl;
									//perror("could not remove file");
								}
							}
						}
					}
				}
				g_free(uri_dir);
				g_free(gfw);
			} 
		}
	}
}

void RadarSimManager::translate_from_zoomChange(double zoom_factor)
{
	double rx, ry, x, y;

	//cout << "RadarSimManager::translate_from_zoomChange - zoom_factor=" << zoom_factor << " isShown=" << isShown << endl;

	if( !isShown ) return;

	if( site_group_h && !goo_canvas_item_is_visible( site_group_h ) ) {
		if( site_group ) {
			goo_canvas_item_remove(site_group);
			site_group = NULL;
		}
	}

	// loop through radar sites
	for(unsigned int ind=0; ind < g_list_length(radar_list); ind++) {
		// get the site
		NexradStations_t *station = (NexradStations_t *)g_list_nth_data(radar_list,ind);
		//cout << "station: " << station->name << endl;

		if( site_group ) {
			// set radar range
			g_object_get(G_OBJECT(station->range_item), "radius-x", &rx, "radius-y", &ry, NULL);
			rx *= zoom_factor;
			ry *= zoom_factor;
			g_object_set(G_OBJECT(station->range_item), "radius-x", rx, "radius-y", ry, NULL);

			// transform into window
			y = station->lat;
			x = station->lon;
			gps_map->geoToLayer(GPSMap::RADAR_SIM_LAYER, x, y);

			// set site location
			g_object_set (G_OBJECT(station->station_item), "x", x, "y", y, NULL);
		}

		// unload cached images
		for( int i=0; i < NUM_RADAR_TYPES; i++)
			station->loaded_pix[i] = NULL;
	}
		
	return;
}

NexradStations_t * RadarSimManager::findStation( GooCanvasItem *item )
{
	NexradStations_t *station = NULL;
	for(unsigned int ind=0; ind < g_list_length(radar_list); ind++) {
		station = (NexradStations_t *)g_list_nth_data(radar_list,ind);
		if(station && station->station_item == item )
			return station;
	}
	return NULL;
}

void RadarSimManager::setup_item(GooCanvasItem *item)
{
	g_signal_connect (item, "button-press-event", G_CALLBACK (item_button_press_event_cb), this);
	g_signal_connect (item, "button-release-event", G_CALLBACK (item_button_release_event_cb), this);
	g_signal_connect (item, "key-press-event", G_CALLBACK (item_key_event_cb), this);
	g_signal_connect (item, "key-release-event", G_CALLBACK (item_key_event_cb), this);
	g_signal_connect (item, "motion-notify-event", G_CALLBACK (item_motion_notify_event_cb), this);
}

bool RadarSimManager::item_button_press_event_cb (GooCanvasItem *item, GooCanvasItem *target_item, 
		GdkEventButton *event, RadarSimManager *me)
{	
	double mouse_x, mouse_y;

	// these are world coordinates
	mouse_x = event->x;
	mouse_y = event->y;

	//printf("RadarSimManager::item_button_press_event_cb: event->type=%i event->x=%f event->y=%f\n", event->type, event->x, event->y);

	// see if I have a valid pointer
	if( !me ) return false;

	goo_canvas_grab_focus (me->gps_map->getCanvas(), item);

	// find the site the user clicked
	NexradStations_t *station = me->findStation( item );
	if( !station )
		return false;

	switch(event->button) {
		case LEFT_MOUSE:
			//cout << "--> " << station->icao << endl;
			break;

		case RIGHT_MOUSE:
			break;

		case MIDDLE_MOUSE:
			me->active_station = station;
			me->create_radar_window( );	
			return true;

		default:
			printf("Unknown mouse button value\n");
			break;
	} 

	// Returning false propagates the event to parent items;
	// returning TRUE ends event propagation. 
	return false;
}

bool RadarSimManager::item_key_event_cb (GooCanvasItem  *item, GooCanvasItem  *target_item, 
		GdkEventKey *event, RadarSimManager *me)
{
	printf("RadarSimManager::item_key_event_cb: event->type=%i event->keyval=%i\n", event->type, event->keyval);
	//fflush(stdout);

	// see if I have a valid pointer
	if( !me ) return false;

	// check for up/down/left/right arrow key press 
	switch (event->keyval)
	{
		case GDK_Up:
			goo_canvas_item_raise(item,NULL);
			break;
		case GDK_Down:
			goo_canvas_item_lower(item,NULL);
			break;   
		default:
			return false; // propogate event 
	} 

	// make sure item is now null, button was released
	return TRUE;
}


bool RadarSimManager::item_button_release_event_cb (GooCanvasItem  *item, GooCanvasItem  *target_item, 
		GdkEventButton *event, RadarSimManager *me)
{
	//printf("RadarSimManager::item_button_release_event_cb: event->type=%i event->x=%f event->y=%f\n", event->type, event->x, event->y);
	
	// see if I have a valid pointer
	if( !me ) return false;

	// make sure item is now null, button was released
	return false;
}

bool RadarSimManager::item_motion_notify_event_cb (GooCanvasItem  *item, GooCanvasItem  *target_item, 
		GdkEventMotion *event, RadarSimManager *me)
{
	//printf("RadarSimManager::item_motion_event_cb: event->type=%i event->x=%f event->y=%f\n", event->type, event->x, event->y);

	// see if I have a valid pointer
	if( !me ) return false;

	return false;
}

bool RadarSimManager::setShown(int setTo)
{
	//cout << "RadarSimManager::setShown setTo= " << setTo << endl;
	LayerManager::setShown(setTo);

	if(!setTo) {
		//if( site_group_h )
			//g_object_set(site_group_h,"visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL);
		if( scale_item ) 
			goo_canvas_item_remove(scale_item);
		scale_item = NULL;
	}

	if( site_group && !setTo) {
		goo_canvas_item_remove(site_group);
		site_group   = NULL;
	} else if ( setTo && !site_group ) {
		create_canvas();
	}

	return true;
}

bool RadarSimManager::on_focus_in (GooCanvasItem *item, GooCanvasItem *target, GdkEventFocus *event, gpointer data)
{
	RadarSimManager *radar = (RadarSimManager *)data;
	if( !radar ) return false;

	goo_canvas_grab_focus (radar->gps_map->getCanvas(), item);
	g_object_set (item, "stroke-color", "black", NULL);

	return false;
}

bool RadarSimManager::on_focus_out (GooCanvasItem *item, GooCanvasItem *target, GdkEventFocus *event, gpointer data)
{
	g_object_set (item, "stroke-color-rgba", 0x11111144, NULL);
	return false;
}

void RadarSimManager::create_radar_window ()
{
	//cout << "RadarSimManager::create_radar_window()" << endl;
	if( guiStr.builder == NULL ) {
		cout << "create_param_window() - no builder for gui" << endl;
		return;
	}

	GtkWidget *apply_button = GTK_WIDGET (gtk_builder_get_object (guiStr.builder, "applyButton1"));
	GtkWidget *cancel_button = GTK_WIDGET (gtk_builder_get_object (guiStr.builder, "cancelButton1"));

	// create window and setup signals
	if( !radar_window ) {
		radar_window = GTK_WIDGET (gtk_builder_get_object (guiStr.builder, "radar_dialog"));

		// callbacks / signals
		g_signal_connect (GTK_OBJECT(radar_window), "delete_event", G_CALLBACK(radar_window_destroy), radar_window);
		g_signal_connect (GTK_OBJECT(apply_button), "clicked", G_CALLBACK(radar_button_clicked), this);
		g_signal_connect (GTK_OBJECT(cancel_button), "clicked", G_CALLBACK(radar_cancel_clicked), this);
	}

	if( !radar_window || !apply_button || !cancel_button ){
		cout << "Invalid xml ui file for radar" << endl;
		return;
	}

	if( !active_station ) return;

	set_check_butons();

	gtk_window_set_title(GTK_WINDOW(radar_window), active_station->name.c_str());

	gtk_widget_show_all (radar_window);
}

void RadarSimManager::radar_cancel_clicked(GtkWidget *w, gpointer user_data)
{
	RadarSimManager *me = (RadarSimManager *)user_data;
	if( !me ) return;

	// hide the window, it is not destroyed
	gtk_widget_hide(me->radar_window);
}

void RadarSimManager::radar_button_clicked(GtkWidget *w, gpointer user_data)
{
	//printf( " -- radar_button_clicked -- \n");

	RadarSimManager *me = (RadarSimManager *)user_data;
	if( !me ) return;

	uint32_t radarType = 0;
	uint32_t timeLoop  = 0;

	GtkToggleButton *button;
	if( me->active_station ) {

	//-- time-loop buttons --//
		button = GTK_TOGGLE_BUTTON (gtk_builder_get_object (guiStr.builder, "check_nor"));
		if( gtk_toggle_button_get_active(button) ) {
			radarType |= (0x1 << RADAR_NOR);
			if( !gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON (gtk_builder_get_object (guiStr.builder, "nor_snap")) ) )
				timeLoop |= (0x1 << RADAR_NOR);
		}

		button = GTK_TOGGLE_BUTTON (gtk_builder_get_object (guiStr.builder, "check_noz"));
		if( gtk_toggle_button_get_active(button) ) {
			radarType |= (0x1 << RADAR_NOZ);
			if( !gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON (gtk_builder_get_object (guiStr.builder, "noz_snap")) ) )
				timeLoop |= (0x1 << RADAR_NOZ);
		}

		button = GTK_TOGGLE_BUTTON (gtk_builder_get_object (guiStr.builder, "check_ncr"));
		if( gtk_toggle_button_get_active(button) ) {
			radarType |= (0x1 << RADAR_NCR);
			if( !gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON (gtk_builder_get_object (guiStr.builder, "ncr_snap")) ) )
				timeLoop |= (0x1 << RADAR_NCR);
		}

		button = GTK_TOGGLE_BUTTON (gtk_builder_get_object (guiStr.builder, "check_nov"));
		if( gtk_toggle_button_get_active(button) ) {
			radarType |= (0x1 << RADAR_N0V);
			if( !gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON (gtk_builder_get_object (guiStr.builder, "nov_snap")) ) )
				timeLoop |= (0x1 << RADAR_N0V);
		}

		button = GTK_TOGGLE_BUTTON (gtk_builder_get_object (guiStr.builder, "check_nos"));
		if( gtk_toggle_button_get_active(button) ) {
			radarType |= (0x1 << RADAR_NOS);
			if( !gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON (gtk_builder_get_object (guiStr.builder, "nos_snap")) ) )
				timeLoop |= (0x1 << RADAR_NOS);
		}

		button = GTK_TOGGLE_BUTTON (gtk_builder_get_object (guiStr.builder, "check_n1p"));
		if( gtk_toggle_button_get_active(button) ) {
			radarType |= (0x1 << RADAR_N1P);
			if( !gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON (gtk_builder_get_object (guiStr.builder, "n1p_snap")) ) )
				timeLoop |= (0x1 << RADAR_N1P);
		}

		button = GTK_TOGGLE_BUTTON (gtk_builder_get_object (guiStr.builder, "check_ntp"));
		if( gtk_toggle_button_get_active(button) ) {
			radarType |= (0x1 << RADAR_NTP);
			if( !gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON (gtk_builder_get_object (guiStr.builder, "ntp_snap")) ) )
				timeLoop |= (0x1 << RADAR_NTP);
		}		
		
		button = GTK_TOGGLE_BUTTON (gtk_builder_get_object (guiStr.builder, "check_warn"));
		if( gtk_toggle_button_get_active(button) ) 
			radarType |= (0x1 << RADAR_WARN);

		me->subscribeToRadar(radarType, timeLoop);
	}

	// hide the window, it is not destroyed
	gtk_widget_hide(me->radar_window);
}

void RadarSimManager::set_check_butons()
{
	GtkToggleButton *button;

	button = GTK_TOGGLE_BUTTON (gtk_builder_get_object (guiStr.builder, "check_nor"));
	gtk_toggle_button_set_active(button, active_station->getSubscribed(RADAR_NOR));

	button = GTK_TOGGLE_BUTTON (gtk_builder_get_object (guiStr.builder, "check_noz"));
	gtk_toggle_button_set_active(button, active_station->getSubscribed(RADAR_NOZ));

	button = GTK_TOGGLE_BUTTON (gtk_builder_get_object (guiStr.builder, "check_ncr"));
	gtk_toggle_button_set_active(button, active_station->getSubscribed(RADAR_NCR));

	button = GTK_TOGGLE_BUTTON (gtk_builder_get_object (guiStr.builder, "check_nov"));
	gtk_toggle_button_set_active(button, active_station->getSubscribed(RADAR_N0V));

	button = GTK_TOGGLE_BUTTON (gtk_builder_get_object (guiStr.builder, "check_nos"));
	gtk_toggle_button_set_active(button, active_station->getSubscribed(RADAR_NOS));

	button = GTK_TOGGLE_BUTTON (gtk_builder_get_object (guiStr.builder, "check_n1p"));
	gtk_toggle_button_set_active(button, active_station->getSubscribed(RADAR_N1P));

	button = GTK_TOGGLE_BUTTON (gtk_builder_get_object (guiStr.builder, "check_ntp"));
	gtk_toggle_button_set_active(button, active_station->getSubscribed(RADAR_NTP));

	button = GTK_TOGGLE_BUTTON (gtk_builder_get_object (guiStr.builder, "check_warn"));
	gtk_toggle_button_set_active(button, active_station->getSubscribed(RADAR_WARN));
}

// If you return false in the "delete_event" signal handler, GTK will emit
// the "destroy" signal. Returning TRUE means you don't want the window to be
// destroyed.  This is useful for popping up 'are you sure you want to quit?'
// type dialogs.
gboolean RadarSimManager::radar_window_destroy(GtkWidget * widget, GdkEventKey * event, gpointer user_data)
{
	if( user_data ) {
		gtk_widget_hide_all ( GTK_WIDGET(user_data) );
		return TRUE;
	}
	return false;
}

void RadarSimManager::subscribeToRadar( uint32_t setTypes, uint32_t timeLoop )
{
	//cout << "RadarSimManager::subscribeToRadar" << endl;

	bool ifNoDelete = false;
	if( !active_station ) return;

	for(int i=0; i < NUM_RADAR_TYPES; i++) {

		// calculate the uri to download  
		char *uri;
		char *uri_dir = replace_uri(RADAR_CACHE_DIR, active_station->icao.substr(1,3).c_str(), radarTypes[i].type);

		if( i == RADAR_WARN ) {
			uri = replace_uri(LFTP_RADAR_WARNINGS, active_station->icao.substr(1,3).c_str(), radarTypes[i].type);
		} else if( (timeLoop & (0x1 << i)) ) {
			uri = replace_uri(LFTP_RADAR, active_station->icao.substr(1,3).c_str(), radarTypes[i].type);
		} else {
			uri = replace_uri(LFTP_RADAR_BASE, active_station->icao.substr(1,3).c_str(), radarTypes[i].type);
		}

		//-- remove subscription --//
		if( ( !(setTypes & (0x1 << i)) && active_station->getSubscribed((RadarImgTypes)i) ) ||
		  ( ( (setTypes & (0x1 << i)) && active_station->getSubscribed((RadarImgTypes)i) ) && 
			 ( active_station->getLooped((RadarImgTypes)i) != ((timeLoop & (0x1 << i)) >> i) ) ) 
		) {
			cout << "Removing subscription to: "<< active_station->icao << ":" << radarTypes[i].type << endl;
			active_station->setSubscribed((RadarImgTypes)i,false);
			//active_station->setLooped((RadarImgTypes)i,false);
			
			//-- remove any references to this inactive layers
			if( active_station->img_item_h[i] ) {
				gps_map->delLayer( active_station->img_item_h[i] );
				// unload cached images
				goo_canvas_item_remove( active_station->img_item_h[i] );
				active_station->img_item_h[i] = NULL;
				active_station->img_item[i]   = NULL;
				active_station->loaded_pix[i] = NULL;
			}

			//-- stop and free up the radar web subscription 
			unsigned int ind;
			for(ind=0; ind < g_list_length(radar_subs); ind++) {
				char *l_uri = (char *)g_list_nth_data(radar_subs,ind);
				if( strstr( l_uri, active_station->icao.substr(1,3).c_str()) )
					if( strstr( l_uri, radarTypes[i].type) ) {
						radar_subs = g_list_remove(radar_subs, l_uri);
						// free memory
						g_free(l_uri);
						break;
					}
			}
			if( ind == g_list_length(radar_subs) && g_list_length(radar_subs) > 0 )
				cout << "RadarSimManager::subscribeToRadar -- did not find subscription string in radar_subs" << endl;

			RadarImgAlloc_t *alloc = NULL;
			unsigned int f_ind=0;
			while( f_ind < g_list_length(active_station->img_alloc_list[i]) ) { 
				// get the file
				alloc = (RadarImgAlloc_t *)g_list_nth_data(active_station->img_alloc_list[i],f_ind);
				if( alloc ) {
					if( alloc->file.find( uri_dir ) >= 0 ) {
						//cout << "Unloading file: " << alloc->file << endl;
						active_station->img_alloc_list[i] = g_list_remove(active_station->img_alloc_list[i], alloc);
						delete alloc;
						alloc = NULL;

					} else
						f_ind++;
				} else {
					// we really should not get here, but if so clean up gracefully
					cout << "!! No img_alloc at location " << f_ind << endl;
				}
			}
		}

		//-- if new subscription --//
		if( (setTypes & (0x1 << i)) && !active_station->getSubscribed((RadarImgTypes)i) ) {
			cout << "Subscribing to: "<< active_station->icao << ":" << radarTypes[i].type 
			     << " => " << radarTypes[i].name << endl;
			active_station->setSubscribed((RadarImgTypes)i,true);
			active_station->setLooped((RadarImgTypes)i,(timeLoop & (0x1 << i)));
			if( (timeLoop & (0x1 << i)) ) playing = true;

			radar_subs = g_list_append( radar_subs, uri);
			ifNoDelete = true;

			//-- make sure the directory structure exists
			
			string dir = string(RADAR_BASE_DIR) + string(radarTypes[i].type);
			//cout << "mkdir - " << dir << endl;
			if( mkdir(dir.c_str(), 0775) < 0 )
				if( errno != EEXIST ) perror("mkdir");

			//cout << "mkdir - " << uri_dir << endl;
			if( mkdir(uri_dir, 0775) < 0 )
				if( errno != EEXIST ) perror("mkdir");

			//-- create a new image layer for the radar
			if( !active_station->img_layer ) {
				active_station->img_layer = goo_canvas_group_new (img_group, NULL);

				//-- add the image to the layer list
				gps_map->addLayer( active_station->icao.c_str(), active_station->img_layer, 
				               (GooCanvasItem *)GPSMap::RADAR_SIM_LAYER, active_station->name.c_str() );
				gps_map->setLayerListShown(active_station->img_layer, true );
			}
	
			// make sure the map layer manager is shown
			gps_map->toggleLayerShown( GPSMap::RADAR_SIM_LAYER, true);

			//-- get the world file
			char *gfw_uri;
			if( i == RADAR_WARN )
				gfw_uri = replace_uri(LFTP_RADAR_WARNINGS_GFW, active_station->icao.substr(1,3).c_str(), radarTypes[i].type);
			else
				gfw_uri = replace_uri(LFTP_RADAR_BASE_GFW, active_station->icao.substr(1,3).c_str(), radarTypes[i].type);
			char *gfw = replace_uri(RADAR_CACHE_GFW, active_station->icao.substr(1,3).c_str(), radarTypes[i].type);

			// attempt to get the file attributes, i.e. test file existence
			int ret;
			cout << "Download world file: " << gfw << endl;
			ret = system( gfw_uri );
			if( ret < 0 ) perror("system command");

			//-- clean up
			g_free(gfw_uri);
			g_free(gfw);

			//-- get the radar images
			//cout << "Setting up site mirroring : " << endl << "\tURI: " << uri << endl;
			ret = system( uri );
			if( ret < 0 ) perror("system command");
		}

		

		if( !ifNoDelete ) g_free(uri);
		g_free(uri_dir);

		if( timeLoop == 0 ) playing = false;

		if( setTypes == 0 && active_station->img_layer) {
			gps_map->delLayer( active_station->img_layer );
			goo_canvas_item_remove( active_station->img_layer );
			active_station->img_layer = NULL;
		}

	}

}

gboolean RadarSimManager::update_web_timeout(gpointer data)
{
	//cout << "RadarSimManager::update_web_timeout" << endl;
	if( !data ) return false;
	RadarSimManager *me = (RadarSimManager*) data;
	if( !me->radar_subs )
		return true;

	if( !goo_canvas_item_is_visible( me->layerG ) ) 
		return true;

	for(unsigned int ind=0; ind < g_list_length(me->radar_subs); ind++) {
		char *uri = (char *)g_list_nth_data(me->radar_subs,ind);
		// elstonj -- FIXME this should not be static
		char *gfw_uri = "lftp -c \"cache off; get http://128.138.75.190/RadarImg/NCR/SIM_NCR_0.gfw -o /usr/local/share/vcgui/radar/RadarImg/NCR/SIM.gfw\" 2> /dev/null";

		if( uri ) {
			//cout << "Updating radar subscription: " << ind << endl << "\t" << uri << endl;
			
			int ret = system( gfw_uri );
			if( ret < 0 ) perror("system command");

			ret = system( uri );
			if( ret < 0 ) perror("system command");
		}
		else 
			cout << "!! No URI for subscription: " << ind << endl;
	}
	
	return true;
}

uint32_t RadarSimManager::inspect_map_uri(const char *uri)
{
	uint32_t uri_format = 0;
	if (g_strrstr(uri, URI_MARKER_S))
		uri_format |= URI_HAS_S;

	if (g_strrstr(uri, URI_MARKER_D))
		uri_format |= URI_HAS_D;

	//g_debug("URI Format: 0x%X", uri_format);
	return uri_format;
}

char * RadarSimManager::replace_uri(const char *uri, const char *site, const char *type)
{        
	char *url;
	unsigned int i = 1;

	uint32_t uri_format = inspect_map_uri(uri);

	url = g_strdup(uri);
	while (i < URI_FLAG_END)
	{
		char *s;
		char *old;

		old = url;
		switch(i & uri_format)
		{
			case URI_HAS_S:
				s = g_strdup_printf("%s", site);
				url = replace_string(url, URI_MARKER_S, s);
				break;
			case URI_HAS_D:
				s = g_strdup_printf("%s", type);
				url = replace_string(url, URI_MARKER_D, s);
				break;
			default:
				s = NULL;
				break;
		}

		if (s) {
			g_free(s);
			g_free(old);
		}

		i = (i << 1);
	}

	return url;
}

RadarImgAlloc_t * RadarSimManager::addImage(string file, string gfw)
{
	//cout << "RadarSimManager::addImage file=" << file << endl;

	GeoImage *radar = new GeoImage();

	if( !radar ) {
		cerr << "RadarSimManager::addImage -  could not create new GeoRefMap() "<< endl;
		return NULL;
	}

	RadarImgAlloc_t *imgAlloc = NULL;

	if( radar->Open(file.c_str(), gfw.c_str()) ) {
		imgAlloc = new RadarImgAlloc_t;
		if( imgAlloc ) {
			imgAlloc->geoImg = radar;
			imgAlloc->file   = file.c_str();
		}
	} 

	if( !imgAlloc )
		delete radar;

	return imgAlloc;
}

void RadarSimManager::setup_scale(time_t startT, time_t userMinT, time_t currT, time_t stopT)
{
	GooCanvasItem *item;
	double x0 = 20; 
	double y0 = 20;
	double h  = 10;
	double w  = 100;

	struct tm time_tm = {0};

	(void *) localtime_r(&startT, &time_tm);
	char * start_str = g_strdup_printf("%i:%02i", time_tm.tm_hour, time_tm.tm_min);
	(void *) localtime_r(&stopT, &time_tm);
	char * stop_str = g_strdup_printf("%i:%02i", time_tm.tm_hour, time_tm.tm_min);
	(void *) localtime_r(&currT, &time_tm);
	char * curr_str = g_strdup_printf("%i:%02i", time_tm.tm_hour, time_tm.tm_min);

	int diff = (stopT - userMinT);
	int hr  = diff / 3600; 
	int min = (diff / 60) % 60; 
	char * umin_str = g_strdup_printf("-%i:%02i", hr, min);

	//cout << "startT=" <<startT << " -- " << start_str << " isdst=" << time_tm->tm_isdst << endl;
	//cout << "stopT=" <<stopT << " -- " << stop_str << endl;

	// to refresh scale item, we simply delete items and start over
	//if( scale_item ) goo_canvas_item_remove(scale_item);
	//scale_item = NULL;
	
	if( !scale_item ) { 
		scale_item = GOO_CANVAS_ITEM(goo_canvas_group_new (scale_item_h, NULL));

		// draw the start leg
		item = goo_canvas_polyline_new_line( scale_item,
				x0, y0 - h,
				x0, y0 + h,
				"line-width", 2.0,
				"stroke_color", "black",
				NULL);

		startT_text = goo_canvas_text_new (scale_item, start_str,
				x0, y0 + h + 5, -1,
				GTK_ANCHOR_CENTER,
				"font", "Sans 8px",
				"fill-color", "black",
				NULL);

		// draw the end leg
		item = goo_canvas_polyline_new_line( scale_item,
				x0 + w, y0 - h,
				x0 + w, y0 + h,
				"line-width", 2.0,
				"stroke_color", "black",
				NULL);

		stopT_text = goo_canvas_text_new (scale_item, stop_str,
				x0 + w, y0 + h + 5, -1,
				GTK_ANCHOR_CENTER,
				"font", "Sans 8px",
				"fill-color", "black",
				NULL);

		// draw the horizontal component
		item = goo_canvas_polyline_new_line( scale_item,
				x0, y0,
				x0 + w, y0,
				"line-width", 2.0,
				"stroke_color", "black",
				NULL);

		// draw the min-start leg
		double xT;
		if(stopT == startT) xT = 0;
		else xT = w *(1 - (stopT - userMinT) / (double)(stopT - startT));
		uminT_item = goo_canvas_polyline_new_line( scale_item,
				x0 + xT, y0 - h,
				x0 + xT, y0 + h,
				"line-width", 2.5,
				"stroke_color", "red",
				NULL);

		uminT_text = goo_canvas_text_new (scale_item, umin_str,
				x0 + xT - 2, y0 - h, -1,
				GTK_ANCHOR_NORTH_EAST,
				"font", "Sans 8px",
				"fill-color", "black",
				NULL);

		// draw the current time leg
		if(stopT == startT) xT = 0;
		else xT = w *(1 - (stopT - currT) / (double)(stopT - startT));
		h -= 2;
		currT_item = goo_canvas_polyline_new_line( scale_item,
				x0 + xT, y0 - h,
				x0 + xT, y0 + h,
				"line-width", 2.5,
				"stroke_color", "blue",
				NULL);

		currT_text = goo_canvas_text_new (scale_item, curr_str,
				x0 + xT, y0 - h - 5, -1,
				GTK_ANCHOR_CENTER,
				"font", "Sans 8px",
				"fill-color", "black",
				NULL);

		// buttons
		GdkPixbuf *pixbuf;
		char name[32];

		w += 10;

	#define SPACING 0
	#define BUTTON_SIZE GTK_ICON_SIZE_LARGE_TOOLBAR

		pixbuf = gtk_widget_render_icon (GTK_WIDGET(gps_map->getCanvas()), 
				GTK_STOCK_MEDIA_REWIND, BUTTON_SIZE, NULL);
		item = goo_canvas_image_new (scale_item, 
				 pixbuf, 
				 x0 + w, y0 - gdk_pixbuf_get_height(pixbuf)/2.0, 
				 NULL);
		w += gdk_pixbuf_get_width(pixbuf) + SPACING;
		g_object_unref (pixbuf);
		g_signal_connect (item, "button-press-event", G_CALLBACK (radar_media_button_cb), this);
		strcpy(name, "prev");
		g_object_set_data (G_OBJECT (item), "id", name);

		// stop
		pixbuf = gtk_widget_render_icon (GTK_WIDGET(gps_map->getCanvas()), 
				GTK_STOCK_MEDIA_STOP, BUTTON_SIZE, NULL);
		item = goo_canvas_image_new (scale_item, 
				 pixbuf, 
				 x0 + w, y0 - gdk_pixbuf_get_height(pixbuf)/2.0, 
				 NULL);
		w += gdk_pixbuf_get_width(pixbuf) + SPACING;
		g_object_unref (pixbuf);
		g_signal_connect (item, "button-press-event", G_CALLBACK (radar_media_button_cb), this);
		strcpy(name, "stop");
		g_object_set_data (G_OBJECT (item), "id", name);

		// start
		pixbuf = gtk_widget_render_icon (GTK_WIDGET(gps_map->getCanvas()), 
				GTK_STOCK_MEDIA_PLAY, BUTTON_SIZE, NULL);
		item = goo_canvas_image_new (scale_item, 
				 pixbuf, 
				 x0 + w, y0 - gdk_pixbuf_get_height(pixbuf)/2.0, 
				 NULL);
		w += gdk_pixbuf_get_width(pixbuf) + SPACING;
		g_object_unref (pixbuf);
		g_signal_connect (item, "button-press-event", G_CALLBACK (radar_media_button_cb), this);
		strcpy(name, "play");
		g_object_set_data (G_OBJECT (item), "id", name);

		// next
		pixbuf = gtk_widget_render_icon (GTK_WIDGET(gps_map->getCanvas()), 
				GTK_STOCK_MEDIA_FORWARD, BUTTON_SIZE, NULL);
		item = goo_canvas_image_new (scale_item, 
				 pixbuf, 
				 x0 + w, y0 - gdk_pixbuf_get_height(pixbuf)/2.0, 
				 NULL);
		g_object_unref (pixbuf);
		g_signal_connect (item, "button-press-event", G_CALLBACK (radar_media_button_cb), this);
		strcpy(name,"next");
		g_object_set_data (G_OBJECT (item), "id", name);
	} 
	// if time scale exists, simply update currT_item and uminT_item	
	else
	{
		if(stopT != startT ) {
			// update current time leg
			double xT = w *(1 - (stopT - currT) / (double)(stopT - startT));

			GooCanvasPoints *points;
			g_object_get (currT_item, "points", &points, NULL);
			points->coords[0] = x0+xT;
			points->coords[2] = x0+xT;
			g_object_set (currT_item, "points", points, NULL);
			goo_canvas_points_unref(points);

			double item_x,item_y;
			g_object_get(currT_text, "x", &item_x, "y", &item_y, NULL);
			g_object_set(currT_text, "x", x0+xT, "y", item_y, NULL);
			g_object_set(currT_text, "text", curr_str, NULL);

			// update user min time leg
			xT = w *(1 - (stopT - userMinT) / (double)(stopT - startT));
			g_object_get (uminT_item, "points", &points, NULL);
			points->coords[0] = x0+xT;
			points->coords[2] = x0+xT;
			g_object_set (uminT_item, "points", points, NULL);
			goo_canvas_points_unref(points);

			// update time on user min time
			g_object_get(uminT_text, "x", &item_x, "y", &item_y, NULL);
			g_object_set(uminT_text, "x", x0+xT-2, "y", item_y, NULL);
			g_object_set(uminT_text, "text", umin_str, NULL);
		} else {
			user_min_time = min_time;
		}

		// update time on start/stop legs
		g_object_set(startT_text, "text", start_str, NULL);
		g_object_set(stopT_text, "text", stop_str, NULL);

	}

	g_free( start_str );
	g_free( stop_str );
	g_free( curr_str );
}

bool RadarSimManager::radar_media_button_cb(GooCanvasItem *item, GooCanvasItem *target_item, GdkEventButton *event, RadarSimManager *radar)
{
	// see if I have a valid pointer
	if( !radar ) return false;
	String name = (char *)g_object_get_data (G_OBJECT (item), "id");

	//cout << "RadarSimManager::radar_media_button_cb - name=" << name << endl;

	if( name == "prev" ) {
		if( !radar->playing ) radar->update_display_time(-1,true);
		else radar->timeScale /= 2.0;
	} else if( name == "stop" ) {
		radar->playing = false;
	} else if( name == "play" ) {
		radar->playing = true;
	} else if( name == "next" ) {
		if( !radar->playing ) radar->update_display_time(1,true);
		else radar->timeScale *= 2.0;
	}

	// returning TRUE ends event propagation. 
	return true;
}

